package com.seckill.service.impl;

import com.seckill.activemq.ActivemqSender;
import com.seckill.dao.MiaoshaOrderDao;
import com.seckill.domain.MiaoshaOrder;
import com.seckill.domain.MiaoshaUser;
import com.seckill.domain.OrderInfo;
import com.seckill.exception.GlobalException;
import com.seckill.rabbitmq.MiaoshaMessage;
import com.seckill.redis.MiaoshaKey;
import com.seckill.redis.OrderKey;
import com.seckill.redis.RedisService;
import com.seckill.result.CodeMsg;
import com.seckill.service.GoodsService;
import com.seckill.service.MiaoshaOrderService;
import com.seckill.service.OrderService;
import com.seckill.vo.GoodsVo;
import com.seckill.vo.OrderTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Objects;

/**
 * @author yangjunwei
 * @date 2021/11/2 9:38 上午
 */
@Slf4j
@Service
public class MiaoshaOrderServiceImpl implements MiaoshaOrderService {

    @Autowired
    GoodsService goodsService;

    @Autowired
    OrderService orderService;

    @Autowired
    RedisService redisService;

    @Autowired
    MiaoshaOrderDao miaoshaOrderDao;

    @Autowired
    ActivemqSender mqSender;

    /**
     * 创建秒杀订单 判断库存->是否秒杀过->减库存->创建订单->创建秒杀订单->订单支付倒计时
     *
     * @param miaoshaMessage 秒杀信息
     */
    @Override
    public void doMiaoshaOrder(MiaoshaMessage miaoshaMessage) {
        MiaoshaUser user = miaoshaMessage.getUser();
        long goodsId = miaoshaMessage.getGoodsId();
        GoodsVo goodsVo = goodsService.selectGoodsVoById(goodsId);
        try {
            //1.判断库存(走数据库）（redis缓存中预减库存，在创建订单的时候，再在数据库进行减少库存的操作）
            checkGoodsStock(goodsVo);
            //2.判断是否秒杀过(还是走redis缓存）
            checkUserMiaosha(goodsId, user.getId());
            //3.减库存(超卖)
            decrStockDB(goodsId);
            //4.基础订单
            OrderInfo order = addOrder(goodsVo, user);
            //5.新增秒杀订单（添加缓存）
            addMiaoshaOrder(goodsId, user.getId(), order.getId());
            //6.订单限时支付（默认30分钟）
            orderCountdown(order.getId(), miaoshaMessage.getOrderType());
        } catch (GlobalException e) {
            CodeMsg cm = e.getCm();
            log.error("doMiaoshaOrder error!{}", cm.getMsg());
            //缓存秒杀失败
            redisService.set(MiaoshaKey.isGoodsOver, MiaoshaKey.getMiaoshaKey(goodsId, user.getId()), true);
        }
    }

    /**
     * 查询秒杀订单
     */
    @Override
    public MiaoshaOrder selectUserOrderForCache(long goodsId, long userId) {
        //秒杀成功下订单后会添加该缓存
        return redisService.get(OrderKey.getMiaoshaOrderByUidGid, OrderKey.getMiaoshaOrderKey(goodsId, userId), MiaoshaOrder.class);
    }

    /**
     * 删除用户缓存中的秒杀订单
     */
    @Override
    public boolean deleteUserGoodsCache(long goodsId, long userId) {
        return redisService.delete(OrderKey.getMiaoshaOrderByUidGid, OrderKey.getMiaoshaOrderKey(goodsId, userId));
    }

    /**
     * 校验库存
     *
     * @param goodsVo 商品Id
     */
    private void checkGoodsStock(GoodsVo goodsVo) {
        if (Objects.isNull(goodsVo)) {
            //库存不足
            throw new GlobalException(CodeMsg.GOODS_NOT_EXIST);
        }
        if (Objects.isNull(goodsVo.getStockCount()) || goodsVo.getGoodsStock() <= 0) {
            throw new GlobalException(CodeMsg.INVENTORY_SHORTAGE);
        }
        log.info("商品库存正常！");
    }

    @Override
    public void checkUserMiaosha(Long goodsId, Long userId) {
        MiaoshaOrder cacheMiaoshaOrder = this.selectUserOrderForCache(goodsId, userId);
        if (Objects.nonNull(cacheMiaoshaOrder)) {
            //不能重复秒杀
            throw new GlobalException(CodeMsg.REPEATE_MIAOSHA);
        }
        MiaoshaOrder dbMiaoshaOrder = this.selectMiaoshaOrderByParam(goodsId, userId);
        if (Objects.nonNull(dbMiaoshaOrder)) {
            //不能重复秒杀
            throw new GlobalException(CodeMsg.REPEATE_MIAOSHA);
        }
        log.info("商品未被秒杀过！");
    }

    /**
     * 减库存，解决超卖问题
     *
     * @param goodsId 商品Id
     */
    private void decrStockDB(Long goodsId) {
        boolean decrStock = goodsService.decrStock(goodsId);
        if (!decrStock) {
            //减库存失败
            throw new GlobalException(CodeMsg.DB_DESR_STOCK_ERROR);
        }
        log.info("DB减库存成功！");
    }

    private OrderInfo addOrder(GoodsVo goodsVo, MiaoshaUser user) {
        OrderInfo order = orderService.createOrder(goodsVo, user);
        if (Objects.isNull(order) || Objects.isNull(order.getId())) {
            //订单创建失败
            throw new GlobalException(CodeMsg.CREATE_ORDER_ERROR);
        }
        log.info("新增订单成功");
        return order;
    }

    /**
     * 增加秒杀订单 保证一人只能下一单，加唯一索引（用户Id，商品Id）
     *
     * @param userId  用户Id
     * @param orderId 订单Id
     * @param goodsId 商品Id
     */
    private void addMiaoshaOrder(Long userId, Long orderId, Long goodsId) {
        MiaoshaOrder miaoshaOrder = MiaoshaOrder.builder().userId(userId).orderId(orderId).goodsId(goodsId).build();
        //唯一索引可能插入失败（数据库中有订单数据，但是redis中没有，因为redis重启数据丢了）
        if (this.insertMiaoshaOrder(miaoshaOrder) < 0) {
            //不能重复秒杀
            throw new GlobalException(CodeMsg.REPEATE_MIAOSHA);
        }
        //添加秒杀订单缓存
        redisService.set(OrderKey.getMiaoshaOrderByUidGid, OrderKey.getMiaoshaOrderKey(goodsId, userId), miaoshaOrder);
    }

    /**
     * 订单限时支付
     *
     * @param orderId   订单Id
     * @param orderType 订单类型
     */
    private void orderCountdown(Long orderId, Integer orderType) {
        mqSender.scheduleSendOrderMsg(orderId, OrderTypeEnum.getTime(orderType));
    }

    private int insertMiaoshaOrder(MiaoshaOrder miaoshaOrder) {
        try {
            return miaoshaOrderDao.insertMiaoshaOrder(miaoshaOrder);
        } catch (Exception e) {
            log.error("insertMiaoshaOrder error!", e);
            return -1;
        }
    }

    public MiaoshaOrder selectMiaoshaOrderByParam(Long goodsId,Long userId){
        return miaoshaOrderDao.selectMiaoshaOrderByParam(goodsId,userId);
    }

    public int deleteMiaoshaOrder(Long goodsId,Long userId){
        return miaoshaOrderDao.deleteMiaoshaOrder(goodsId,userId);
    }



}
